package Class::Scaffold::Environment::Configurator;

# $Id: Configurator.pm 13653 2007-10-22 09:11:20Z gr $
#
# Base class for environment configurators

use warnings;
use strict;
use Error::Hierarchy::Util qw/assert_defined load_class/;
use Class::Scaffold::Environment::Configurator::Local;

# Don't rely on UNIVERSAL::throw if we defined an AUTOLOAD...
use Error::Hierarchy::Internal::CustomMessage;


our $VERSION = '0.05';


use base 'Class::Scaffold::Accessor';


__PACKAGE__
    ->mk_singleton_constructor(qw(new instance))
    ->mk_array_accessors(qw(configurators))
    ->mk_scalar_accessors(qw(local_configurator));


sub init {
    my $self = shift;
    $self->local_configurator(
        Class::Scaffold::Environment::Configurator::Local->new,
    );
}


sub add_configurator {
    my $self = shift;
    my $type = shift;

    assert_defined $type, 'missing configuration type';

    if ($type eq 'file') {
        my $spec = shift;
        assert_defined $spec, 'missing file configuration spec';

        my ($class, $conf_filename);
        if (index($spec, ';') != -1) {
            ($class, $conf_filename) = split /;/ => $spec;

            assert_defined $_, sprintf(
                "can't determine file configuration class from spec [%s]", $spec)
                for $class, $conf_filename;
        } else {

            # assume a default class, and the spec _is_ the conf file name
            $class = 'Class::Scaffold::Environment::Configurator::File';
            $conf_filename = $spec;
        }

        load_class $class, 0;
        $self->configurators_push($class->new(filename => $conf_filename));

    } elsif ($type eq 'getopt') {
        my $options = shift;
        assert_defined $options, 'missing getopt configuration spec';

        require Class::Scaffold::Environment::Configurator::Getopt;
        $self->configurators_push(
            Class::Scaffold::Environment::Configurator::Getopt->
                new(opt => $options)
        );

    } else {
        throw Error::Hierarchy::Internal::CustomMessage(custom_message =>
            sprintf 'unknown configuration type [%s]', $type);
    }
}


# Define functions and class methods lest they be handled by AUTOLOAD.

sub DEFAULTS { () }
sub FIRST_CONSTRUCTOR_ARGS { () }
sub DESTROY {}


# Ask every configurator in turn; return the first defined answer we're given.

sub AUTOLOAD {
    my $self = shift;
    (my $method = our $AUTOLOAD) =~ s/.*://;

    if (@_) {
        throw Error::Hierarchy::Internal::CustomMessage(custom_message =>
            sprintf 'configuration key [%s] is read-only', $method);
    }

    # The local configurator is special -- it always comes first, no matter
    # which configurators have been specified.

    for my $configurator ($self->local_configurator, $self->configurators) {
        my $answer = $configurator->$method;
        return $answer if defined $answer;
    }
    undef;
}


1;


__END__

{% USE p = PodGenerated %}

=head1 NAME

{% p.package %} - large-scale OOP application support

=head1 SYNOPSIS

    {% p.package %}->new;

=head1 DESCRIPTION

=head1 METHODS

=over 4

{% p.write_methods %}

=back

{% p.write_inheritance %}

{% PROCESS standard_pod %}

=cut

